try to enforce the windows compatibility as guessed from server
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Wed, 12 Feb 2025 22:02:57 +0000 (23:02 +0100)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Wed, 19 Feb 2025 13:12:37 +0000 (13:12 +0000)
for now simple rule to guess if the server has windows naming enforced

if windows naming is enforced, we enforce it for new files

if not, we do not care

for now limited to spaces removal

more to come

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
src/libsync/discovery.cpp
test/syncenginetestutils.cpp
test/syncenginetestutils.h
test/testlocaldiscovery.cpp
test/testsyncengine.cpp
test/testsyncvirtualfiles.cpp

index aa669681057a0e534d4db55f58d7e5462eac3c4e..1e325d39d22807c62f27b91047d948485ac8198e 100644 (file)
@@ -290,6 +290,7 @@ bool ProcessDirectoryJob::handleExcluded(const QString &path, const Entries &ent
     const auto hasLeadingOrTrailingSpaces = excluded == CSYNC_FILE_EXCLUDE_LEADING_SPACE
                                             || excluded == CSYNC_FILE_EXCLUDE_TRAILING_SPACE
                                             || excluded == CSYNC_FILE_EXCLUDE_LEADING_AND_TRAILING_SPACE;
+
     const auto leadingAndTrailingSpacesFilesAllowed = !_discoveryData->_shouldEnforceWindowsFileNameCompatibility || _discoveryData->_leadingAndTrailingSpacesFilesAllowed.contains(_discoveryData->_localDir + path);
     if (hasLeadingOrTrailingSpaces && (wasSyncedAlready || leadingAndTrailingSpacesFilesAllowed)) {
         excluded = CSYNC_NOT_EXCLUDED;
index 30c6254e5bfb6dcd7722dbd6b7f8b3e56821e967..637c9099c415ce43ad8a9e8d5f5f31e6949bdca3 100644 (file)
@@ -1207,6 +1207,81 @@ void FakeFolder::switchToVfs(QSharedPointer<OCC::Vfs> vfs)
     vfs->start(vfsParams);
 }
 
+void FakeFolder::enableEnforceWindowsFileNameCompatibility()
+{
+    syncEngine().account()->setCapabilities(QVariantMap{
+        {
+            "files",
+            QVariantMap{
+                {
+                    "forbidden_filename_basenames",
+                    QStringList{"con",
+                        "prn",
+                        "aux",
+                        "nul",
+                        "com0",
+                        "com1",
+                        "com2",
+                        "com3",
+                        "com4",
+                        "com5",
+                        "com6",
+                        "com7",
+                        "com8",
+                        "com9",
+                        "com¹",
+                        "com²",
+                        "com³",
+                        "lpt0",
+                        "lpt1",
+                        "lpt2",
+                        "lpt3",
+                        "lpt4",
+                        "lpt5",
+                        "lpt6",
+                        "lpt7",
+                        "lpt8",
+                        "lpt9",
+                        "lpt¹",
+                        "lpt²",
+                        "lpt³"
+                    }
+                },
+                {
+                    "forbidden_filename_characters",
+                    QStringList{"\\",
+                        "/",
+                        "<",
+                        ">",
+                        ":",
+                        "\"",
+                        "|",
+                        "?",
+                        "*",
+                        "\\",
+                        "/"
+                    }
+                },
+                {
+                    "forbidden_filename_extensions",
+                    QStringList{" ",
+                        ".",
+                        ".filepart",
+                        ".part",
+                        ".part"
+                    }
+                },
+                {
+                    "forbidden_filenames",
+                    QStringList{"\\",
+                        ".htaccess"
+                    }
+                }
+            }
+        }
+    });
+}
+
 FileInfo FakeFolder::currentLocalState()
 {
     QDir rootDir { _tempDir.path() };
index b188a2d00a8aa4ee8763d03aceb2481d52c00ace..817a138a58a3fadad9d53a9b423a13c2f026e730 100644 (file)
@@ -559,6 +559,8 @@ public:
 
     void switchToVfs(QSharedPointer<OCC::Vfs> vfs);
 
+    void enableEnforceWindowsFileNameCompatibility();
+
     [[nodiscard]] OCC::AccountPtr account() const { return _account; }
     [[nodiscard]] OCC::SyncEngine &syncEngine() const { return *_syncEngine; }
     [[nodiscard]] OCC::SyncJournalDb &syncJournal() const { return *_journalDb; }
index 3f4325a6c27e537237b5ddd92050e04e15c5a3b2..43a624eaa352e11fdf57a9937569ff2d3cf7cf44 100644 (file)
@@ -371,7 +371,10 @@ private slots:
     void testCreateFileWithTrailingSpaces_localAndRemoteTrimmedDoNotExist_renameAndUploadFile()
     {
         FakeFolder fakeFolder{FileInfo{}};
+        fakeFolder.enableEnforceWindowsFileNameCompatibility();
+
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
         const QString fileWithSpaces1(" foo");
         const QString fileWithSpaces2(" bar ");
         const QString fileWithSpaces3("bla ");
@@ -459,6 +462,7 @@ private slots:
     void testCreateLocalPathsWithLeadingAndTrailingSpaces_syncOnSupportingOs()
     {
         FakeFolder fakeFolder{FileInfo()};
+        fakeFolder.enableEnforceWindowsFileNameCompatibility();
         fakeFolder.localModifier().mkdir("A");
         QVERIFY(fakeFolder.syncOnce());
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -541,6 +545,7 @@ private slots:
     void testCreateFileWithTrailingSpaces_localTrimmedAlsoCreated_dontRenameAutomaticallyAndDontUploadFile()
     {
         FakeFolder fakeFolder{FileInfo{}};
+        fakeFolder.enableEnforceWindowsFileNameCompatibility();
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
         const QString fileWithSpaces(" foo");
         const QString fileTrimmed("foo");
index 1a1d55971bc67abeb9e2ef0201c60c458acc7385..02f9668cc7da2b8585b33db9471d5c75aea9aa99 100644 (file)
@@ -2039,6 +2039,8 @@ private slots:
     void testCreateFileWithTrailingLeadingSpaces_local_automatedRenameBeforeUpload()
     {
         FakeFolder fakeFolder{FileInfo{}};
+        fakeFolder.enableEnforceWindowsFileNameCompatibility();
+
         fakeFolder.syncEngine().setLocalDiscoveryEnforceWindowsFileNameCompatibility(true);
 
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
index e471c557322cc731182a20afe10fee78fb3da823..f6281ab38df7143bf6d34f58abfd2e1f4b1d28a5 100644 (file)
@@ -835,12 +835,21 @@ private slots:
 
         QVERIFY(fakeFolder.syncOnce());
 
+#if defined Q_OS_WINDOWS
         QCOMPARE(completeSpy.findItem(fileWithSpaces1)->_status, SyncFileItem::Status::FileNameInvalid);
         QCOMPARE(completeSpy.findItem(fileWithSpaces2)->_status, SyncFileItem::Status::FileNameInvalid);
         QCOMPARE(completeSpy.findItem(fileWithSpaces3)->_status, SyncFileItem::Status::FileNameInvalid);
         QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::FileNameInvalid);
         QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::FileNameInvalid);
         QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::FileNameInvalid);
+#else
+        QCOMPARE(completeSpy.findItem(fileWithSpaces1)->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces2)->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces3)->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::Success);
+#endif
 
         fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces1);
         fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces2);
@@ -853,12 +862,67 @@ private slots:
 
         QVERIFY(fakeFolder.syncOnce());
 
-        QCOMPARE(completeSpy.findItem(fileWithSpaces1)->_status, SyncFileItem::Status::Success);
-        QCOMPARE(completeSpy.findItem(fileWithSpaces2)->_status, SyncFileItem::Status::Success);
-        QCOMPARE(completeSpy.findItem(fileWithSpaces3)->_status, SyncFileItem::Status::Success);
-        QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::Success);
-        QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::Success);
-        QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::Success);
+#if defined Q_OS_WINDOWS
+        QCOMPARE(completeSpy.findItem(QStringLiteral("foo"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("bar"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("bla"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/foo"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/bar"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/bla"))->_status, SyncFileItem::Status::Success);
+#endif
+    }
+
+    void testCreateFileWithTrailingSpaces_acceptAndRejectInvalidFileName_enforceWindowsNamingRules()
+    {
+        FakeFolder fakeFolder{ FileInfo() };
+        fakeFolder.enableEnforceWindowsFileNameCompatibility();
+        setupVfs(fakeFolder);
+        QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+        const QString fileWithSpaces1(" foo");
+        const QString fileWithSpaces2(" bar ");
+        const QString fileWithSpaces3("bla ");
+        const QString fileWithSpaces4("A/ foo");
+        const QString fileWithSpaces5("A/ bar ");
+        const QString fileWithSpaces6("A/bla ");
+
+        fakeFolder.localModifier().insert(fileWithSpaces1);
+        fakeFolder.localModifier().insert(fileWithSpaces2);
+        fakeFolder.localModifier().insert(fileWithSpaces3);
+        fakeFolder.localModifier().mkdir("A");
+        fakeFolder.localModifier().insert(fileWithSpaces4);
+        fakeFolder.localModifier().insert(fileWithSpaces5);
+        fakeFolder.localModifier().insert(fileWithSpaces6);
+
+        ItemCompletedSpy completeSpy(fakeFolder);
+        completeSpy.clear();
+
+        QVERIFY(fakeFolder.syncOnce());
+
+        QCOMPARE(completeSpy.findItem(fileWithSpaces1)->_status, SyncFileItem::Status::FileNameInvalid);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces2)->_status, SyncFileItem::Status::FileNameInvalid);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces3)->_status, SyncFileItem::Status::FileNameInvalid);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::FileNameInvalid);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::FileNameInvalid);
+        QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::FileNameInvalid);
+
+        fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces1);
+        fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces2);
+        fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces3);
+        fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces4);
+        fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces5);
+        fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces6);
+
+        completeSpy.clear();
+
+        QVERIFY(fakeFolder.syncOnce());
+
+        QCOMPARE(completeSpy.findItem(QStringLiteral("foo"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("bar"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("bla"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/foo"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/bar"))->_status, SyncFileItem::Status::Success);
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/bla"))->_status, SyncFileItem::Status::Success);
     }
 
     void testCreateFileWithTrailingSpaces_remoteDontGetRenamedAutomatically()